﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using AForge.Imaging.Filters;
using Image = System.Drawing.Image;

namespace WindowsFormsGUI.Entidades
{
    public class ProcesadorDeImagen
    {
        #region Atributos

        private static int _centroMasaX = 0;
        private static int _centroMasaY = 0;
        private static List<ImageMonedaRecordada> _listaDeMonedasRecordadas = new List<ImageMonedaRecordada>();
        private static List<ImagenMonedaProcesada> _listaDeDistancias = new List<ImagenMonedaProcesada>();

        #endregion

        #region Metodos publicos

        public static void Agregar(ImageMonedaRecordada imr)
        {
            _listaDeMonedasRecordadas.Add(imr);
        }

        public static void Entrenar(Image imagen)
        {

        }

        public static Moneda Clasificar(Image imagen)
        {
            var bitmap = new Bitmap(imagen);
            var recorte = ConvertirImagenAMapaDeBit(bitmap);
            // convierto la imagen a un arreglo para poder darsela a la red neuronal
            var arreglo = ConvertirImagenEnArregloDeFloat(recorte);
            // creo una red neuronal de Hopfield para que reconozca la moneda
            var redNeuronal = new HopfieldNetwork(arreglo.Length);

            for (int i = 0; i < 10; i++)
            {
                arreglo = redNeuronal.Present(arreglo);
            }
            // calculo las distancias de las imagenes que reconoce la RNA con la imagen que me llega
            var monedaResultado = ProcesarArreglo(arreglo);

            return ConvertirImagenMonedaAMoneda(monedaResultado); ;
        }

        public static Bitmap ConvertirImagenAMapaDeBit(Bitmap bitmap)
        {
            // blur
            var blur = AplicarBlur(bitmap);
            // escala de grises
            var gris = ObtenerEscalaDeGrises(blur);
            // binarizacion
            var binarizado = Binarizacion(gris);
            // centro de masa
            CalcularCentroDeMasa(binarizado);
            // ecualizacion del histograma
            var equalizado = EcualizacionDelHistograma(binarizado);
            // deteccion de bordes sobel
            var sobel = DeteccionDeBordesSobel(equalizado);
            // recortar imagen
            var recorte = RecortarImagen(sobel);

            var redimensionar = Redimensionar(recorte);

            return redimensionar;
        }

        private static Bitmap Redimensionar(Bitmap bitmap)
        {
            var filtro = new ResizeNearestNeighbor(100, 100);
            return filtro.Apply(bitmap);
        }

        private static Moneda ConvertirImagenMonedaAMoneda(ImagenMonedaProcesada monedaResultado)
        {
            Moneda moneda;

            switch (monedaResultado.LadoMoneda)
            {
                case CaraSecaEnum.CincoCentavosCara:
                case CaraSecaEnum.CincoCentavosSeca:
                    moneda = new Moneda(DenominacionEnum.CincoCentavos, (float) 0.05);
                    break;
                case CaraSecaEnum.DiezCentavosCara:
                case CaraSecaEnum.DiezCentavosSeca:
                    moneda = new Moneda(DenominacionEnum.DiezCentavos, (float)0.10);
                    break;
                case CaraSecaEnum.VeinticincoCentavosCara:
                case CaraSecaEnum.VeinticincoCentavosSeca:
                    moneda = new Moneda(DenominacionEnum.VeinticincoCentavos, (float)0.25);
                    break;
                case CaraSecaEnum.CincuentaCentavosCara:
                case CaraSecaEnum.CincuentaCentavosSeca:
                    moneda = new Moneda(DenominacionEnum.CincuentaCentavos, (float)0.50);
                    break;
                case CaraSecaEnum.UnPesoCara:
                case CaraSecaEnum.UnPesoSeca:
                    moneda = new Moneda(DenominacionEnum.UnPeso, (float)1.00);
                    break;
                default:
                    moneda = null;
                    break;
            }

            return moneda;
        }

        private static ImagenMonedaProcesada ProcesarArreglo(float[] arreglo)
        {
            foreach (var item in _listaDeMonedasRecordadas)
            {
                var distancia = CalcularDistanciaEuclidia(item.MonedaRecordada, arreglo);

                _listaDeDistancias.Add(new ImagenMonedaProcesada(distancia, item.LadoMoneda));
            }

            return _listaDeDistancias.FirstOrDefault(p => p.Distancia == _listaDeDistancias.Min(q => q.Distancia));
        }

        private static double CalcularDistanciaEuclidia(float[] monedaRecordada, float[] arreglo)
        {
            double acumulador = 0;

            for (int i = 0; i < monedaRecordada.Length; i++)
            {
                acumulador += Math.Pow((monedaRecordada[i] - arreglo[i]), 2);
            }

            return Math.Sqrt(acumulador);
        }

        #endregion

        #region Metodos de soporte

        private static Bitmap RecortarImagen(Bitmap bitmap)
        {
            int x0 = 0;
            int y0 = 0;
            int x1 = 0;
            int y1 = 0;
            int x2 = 0;
            int y2 = 0;
            int x3 = 0;
            int y3 = 0;

            bool bandera = false;

            // Recorrer hacia adelante y hacia abajo
            for (int width = 0; width < bitmap.Width; width++)
            {
                for (int height = 0; height < bitmap.Height; height++)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x0 = width;
                        y0 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            bandera = false;
            // Recorrer hacia abajo y hacia adelante
            for (int height = 0; height < bitmap.Height; height++)
            {
                for (int width = 0; width < bitmap.Width; width++)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x1 = width;
                        y1 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            bandera = false;
            // Recorrer hacia atras y hacia arriba
            for (int width = bitmap.Width - 1; width >= 0; width--)
            {
                for (int height = bitmap.Height - 1; height >= 0; height--)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x2 = width;
                        y2 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            bandera = false;
            // Recorrer hacia arriba y hacia atras
            for (int height = bitmap.Height - 1; height >= 0; height--)
            {
                for (int width = bitmap.Width - 1; width >= 0; width--)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x3 = width;
                        y3 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            var filtro = new Crop(new Rectangle(x0, y1, x2 - x0, y3 - y1));
            return filtro.Apply(bitmap);
        }

        public static Bitmap AplicarBlur(Bitmap bitmap)
        {
            var filtro = new Blur();
            return filtro.Apply(bitmap);
        }

        public static Bitmap ObtenerEscalaDeGrises(Bitmap bitmap)
        {
            var filtro = new Grayscale(0.2125, 0.7154, 0.0721);
            return filtro.Apply(bitmap);
        }

        public static Bitmap Binarizacion(Bitmap bitmap)
        {
            var filtro = new Threshold();
            return filtro.Apply(bitmap);
        }

        public static void CalcularCentroDeMasa(Bitmap bitmap)
        {
            _centroMasaX = 0;
            _centroMasaY = 0;

            var contadorPixelNegro = 0;
            var acumuladorX = 0;
            var acumuladorY = 0;

            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    var colorPixel = (bitmap.GetPixel(i, j));

                    if (colorPixel.ToArgb() == Color.Black.ToArgb())
                    {
                        acumuladorX += i;
                        acumuladorY += j;

                        contadorPixelNegro++;
                    }
                }
            }

            if (contadorPixelNegro > 0)
            {
                _centroMasaX = acumuladorX / contadorPixelNegro;
                _centroMasaY = acumuladorY / contadorPixelNegro;
            }
        }

        public static Bitmap EcualizacionDelHistograma(Bitmap bitmap)
        {
            var filtro = new HistogramEqualization();
            return filtro.Apply(bitmap);
        }

        public static Bitmap DeteccionDeBordesSobel(Bitmap bitmap)
        {
            var filtro = new SobelEdgeDetector();
            return filtro.Apply(bitmap);
        }

        public static float[] ConvertirImagenEnArregloDeFloat(Bitmap bitmap)
        {
            var arreglo = new float[bitmap.Width * bitmap.Height];

            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    var colorPixel = (bitmap.GetPixel(i, j));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        arreglo[i + bitmap.Width * j] = 1;
                    }
                    else
                    {
                        arreglo[i + bitmap.Width * j] = -1;
                    }
                }
            }

            return arreglo;
        }

        #endregion
    }
}
